#pragma once
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BY_VALUE 0
#define BY_INDEX 1

#define BY_ASC 0
#define BY_DESC 1

#define S_GMEAN 0
#define S_HMEAN 1
#define S_MAX 2
#define S_MEAN 3
#define S_MIN 4
#define S_PSD 5
#define S_PVAR 6
#define S_QMEAN 7
#define S_SUM 8

#define MAX_DATA 1024

typedef struct List {
	int len, p;
	float data[MAX_DATA];
} List;

List *new_list() {
	char *list = (char *)malloc(sizeof(List));
	if (!list) {
		fprintf(stderr, "malloc() can't allocate memory\n");
		exit(1);
	}
	List l;
	l.len = -1;
	memcpy(list, &l, sizeof(List));
	return (List *)list;
}

void destroy_list(List *list) {
	free(list);
}

int length_list(List *list) {
	return list->len + 1;
}

int append_list(List *list, float value) {
	list->len ++;
	if (list->len > MAX_DATA - 1) {
		return 1;
	}
	list->data[list->len] = value;
	return 0;
}

float get_list(List *list, int index) {
	if ((index >= 0) && (index < length_list(list))) {
		return list->data[index];
	} else {
		return 0;
	}
}

int find_list(List *list, float number, int from) {
	for (int i = (from <= 0? 0: from); i <= list->len; i ++) {
		if (list->data[i] == number) {
			return i;
		}
	}
	return -1;
}

float statistic_list(List *list, int type) {
	float result = 0, mean, pvar;
	switch (type) {
		case S_GMEAN:
			result = 1;
			for (int i = 0; i <= list->len; i ++) {
				result *= list->data[i];
			}
			result = powf(result, 1.0 / length_list(list));
			break;
		case S_HMEAN:
			if (find_list(list, 0, -1) == -1) {
				for (int i = 0; i <= list->len; i ++) {
					result += 1.0 / list->data[i];
				}
				result = length_list(list) / result;
			}
			break;
		case S_MAX:
			result = list->data[0];
			for (int i = 1; i <= list->len; i ++) {
				if (list->data[i] > result) {
					result = list->data[i];
				}
			}
			break;
		case S_MEAN:
			for (int i = 0; i <= list->len; i ++) {
				result += list->data[i];
			}
			result /= length_list(list);
			break;
		case S_MIN:
			result = list->data[0];
			for (int i = 1; i <= list->len; i ++) {
				if (list->data[i] < result) {
					result = list->data[i];
				}
			}
			break;
		case S_PSD:
			pvar = statistic_list(list, S_PVAR);
			result = sqrtf(pvar);
			break;
		case S_PVAR:
			mean = statistic_list(list, S_MEAN);
			for (int i = 0; i <= list->len; i ++) {
				result += powf(list->data[i] - mean, 2);
			}
			result = 1.0 / length_list(list) * result;
			break;
		case S_QMEAN:
			for (int i = 0; i <= list->len; i ++) {
				result += powf(list->data[i], 2);
			}
			result = sqrt(result / length_list(list));
			break;
		case S_SUM:
			for (int i = 0; i<=list->len; i ++) {
				result += list->data[i];
			}
			break;	
		default:
			result = 0;
	}
	return result;
}

int cmp_asc(const void *a, const void *b) {
	int x = *(const float*)a;
	int y = *(const float*)b;
	return (x > y) - (x < y);
}

int cmp_desc(const void *a, const void *b) {
	int x = *(const float*)a;
	int y = *(const float*)b;
	return (x < y) - (x > y);
}

void sort_list(List *list, int type) {
	if (type == BY_ASC) {
		qsort(list->data, length_list(list), sizeof(float), cmp_asc);
	} else if (type == BY_DESC) {
		qsort(list->data, length_list(list), sizeof(float), cmp_desc);
	}
}

void remove_list(List *list, float value, int type) {
	if (type == BY_INDEX) {
		for (int i = 0; i<=list->len; i ++) {
			if (i >= value) {
				list->data[i] = list->data[i + 1];
			}
		}
		list->len --;
	} else if (type == BY_VALUE) {
		int flag = 0;
		for (int i = 0; i<=list->len; i ++) {
			if (list->data[i] == value) {
				flag = 1;
				break;
			}
			if (flag) {
				list->data[i] = list->data[i + 1];
			}
		}
		if (flag)
			list->len --;
	}
}

